local t = {isFinish=false,isPlay=false,stage=0,counters=nil}

t.events = {
    MapHookEvent.OnUnitDead,
    MapHookEvent.OnHookDetach,
}

function main(pMapInstance, pHook)
    t.instance, t.hook = pMapInstance, pHook
    t.handler = pMapInstance:AttachMapHookInfo(t)
    t.vars = pHook:GetVariables()
    t.vars.init = t.Init
    t.vars.play = t.Play
end

function t.Cleanup()
    t.hook:ShutdownMapInstance(30*1000)
    t.instance:ClearAllHostileForcesCreature()
    t.ApplyPlayCleanupStatus()
    RemoveTimer(t.hook, HookTimerType.PlayTimeout)
    RemoveTimer(t.hook, HookTimerType.DelayStage)
end

function t.Init(cfg, questTypeID, backMapType)
    t.cfg = cfg
    t.questTypeID = questTypeID
    t.backMapType = backMapType
end

function t.Play(delay, duration)
    if not t.isPlay then
        t.isPlay = true
        DoItOrDelay(t.hook, function()
            t.DeployStageObject()
            t.ReadyNextStage()
        end, delay*1000)
        CreateTimer(t.hook, function() t.Finish(false) end,
            HookTimerType.PlayTimeout, duration*1000, 1)
    end
end

function t.ReadyNextStage()
    t.stage, t.counters = t.stage + 1, nil
    DoItOrDelay(t.hook, t.DeployStageObject,
        t.cfg.schedule[t.stage].t, HookTimerType.DelayStage)
end

function t.DeployStageObject()
    t.counters = t.stage > 0 and
        {p=t.cfg.schedule[t.stage].p,dirty=false} or nil
    t.DeployStageCreature(t.stage)
    t.DeployStageStaticObject(t.stage)
end

function t.DeployStageCreature(stage)
    local cfgs = t.cfg.deploy.creature
    if cfgs then
        for i, cfg in ipairs(cfgs) do
            if (cfg.i or 0) == stage then
                DoItOrDelay(t.hook, function()
                    t.instance:DeployStageCreature(nil, table.unpack(cfg))
                end, cfg.t, HookTimerType.DelayStage)
            end
        end
    end
end

function t.DeployStageStaticObject(stage)
    local cfgs = t.cfg.deploy.staticobject
    if cfgs then
        for i, cfg in ipairs(cfgs) do
            if (cfg.i or 0) == stage then
                DoItOrDelay(t.hook, function()
                    t.instance:DeployStageStaticObject(nil, table.unpack(cfg))
                end, cfg.t, HookTimerType.DelayStage)
            end
        end
    end
end

function t.ApplyStageCleanupStatus()
    local cfgs = t.cfg.schedule[t.stage]
    t.ApplyCleanupStatus(cfgs)
end

function t.ApplyPlayCleanupStatus()
    local cfgs = t.cfg.cleanup
    t.ApplyCleanupStatus(cfgs)
end

function t.ApplyCleanupStatus(cfgs)
    if cfgs[1] then
        for i, entry in ipairs(cfgs[1]) do
            t.instance:CleanupStageCreature(entry)
        end
    end
    if cfgs[2] then
        for i, entry in ipairs(cfgs[2]) do
            t.instance:CleanupStageStaticObject(entry)
        end
    end
    if cfgs[3] then
        for i = 1, #cfgs[3], 2 do
            t.instance:ApplyNavPhysicsFlags(cfgs[3][i], cfgs[3][i+1])
        end
    end
end

function t.IsStageNotYetPass()
    for entry, num in pairs(t.counters.p) do
        if (t.counters[entry] or 0) < num then
            return true
        end
    end
end

function t.IsLastStage()
    return #t.cfg.schedule <= t.stage
end

function t.TryFinishStage()
    if not t.isFinish and t.counters then
        t.counters.dirty = false
        if not t.IsStageNotYetPass() then
            t.ApplyStageCleanupStatus()
            if not t.IsLastStage() then
                t.ReadyNextStage()
            else
                t.Finish(true)
            end
        end
    end
end

function t.Finish(isSucc)
    if not t.isFinish then
        t.isFinish = true
        t.hook:FinishPlayStory(isSucc, t.questTypeID, t.backMapType, 3000)
        t.instance:DetachMapHookInfo(t.handler)
    end
end

function t.OnUnitDead(pUnit, pKiller)
    if t.counters then
        if pUnit:IsType(TYPE_CREATURE) then
            local entry = pUnit:GetEntry()
            if t.counters.p[entry] then
                t.counters[entry] = (t.counters[entry] or 0) + 1
                if not t.counters.dirty then
                    t.counters.dirty = true
                    t.instance:AddEvent(t.TryFinishStage)
                end
            end
        end
    end
end